package com.ruoyi.framework.aspectj;

import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.domain.SysOperLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

@Aspect
@Component
public class GlobalLogAspect {
    private static final Logger log = LoggerFactory.getLogger(GlobalLogAspect.class);
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");
    
    @Autowired
    private ObjectMapper objectMapper;

    // 敏感字段列表
    private static final Set<String> EXCLUDE_PROPERTIES = new HashSet<>(Arrays.asList(
        "password", "oldPassword", "newPassword", "token", 
        "accessKey", "secretKey", "securityAnswer"
    ));

    private static final Set<String> EXCLUDE_URL_PATTERNS = new HashSet<>(Arrays.asList(
        "/**/operlog/**",
        "/**/system/**"
    ));

    // 定义切入点：所有Controller方法
    @Pointcut("execution(* com.ruoyi..controller..*.*(..)) " +
          "&& !@annotation(com.ruoyi.common.annotation.Log)")
    public void logPointCut() {}

    @Before("logPointCut()")
    public void doBefore() {
        HttpServletRequest request = ServletUtils.getRequest();
        if (isExcludedUrl(request)) {
            return;
        }
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
        try {
            // 获取当前请求
            HttpServletRequest request = ServletUtils.getRequest();
            if (request == null|| isExcludedUrl(request)) {
                log.warn("无法获取HttpServletRequest，跳过日志记录");
                return;
            }

            // 创建操作日志对象
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            operLog.setOperTime(new Date());
            
            // 计算耗时
            if (TIME_THREADLOCAL.get() != null) {
                operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
                TIME_THREADLOCAL.remove();
            } else {
                operLog.setCostTime(0L);
            }

            // 请求信息
            operLog.setOperIp(IpUtils.getIpAddr(request));
            operLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
            operLog.setRequestMethod(request.getMethod());

            // 用户信息
            String username = SecurityUtils.getUsername();
            operLog.setOperName(username != null ? username : "N/A");

            // 方法信息
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");

            // 操作模块（使用类名简写）
            int lastDotIndex = className.lastIndexOf('.');
            operLog.setTitle(lastDotIndex > 0 
                ? className.substring(lastDotIndex + 1) 
                : className);

            // 请求参数（排除敏感字段）
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));

            // 返回结果
            if (jsonResult != null) {
                try {
                    // 简单过滤敏感字段
                    String result = filterSensitiveFields(jsonResult);
                    operLog.setJsonResult(StringUtils.substring(result, 0, 2000));
                } catch (Exception ex) {
                    log.warn("返回结果序列化失败: {}", ex.getMessage());
                    operLog.setJsonResult("【序列化失败】" + jsonResult.getClass().getName());
                }
            }

            // 异常信息
            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                String errorMsg = e.getMessage();
                operLog.setErrorMsg(errorMsg != null 
                    ? StringUtils.substring(errorMsg, 0, 2000) 
                    : "Unknown Error");
            }

            // 业务类型自动推断
            String httpMethod = request.getMethod();
            if ("GET".equals(httpMethod)) {
                operLog.setBusinessType(10); // 查询
                operLog.setOperatorType(10);
            } else if ("POST".equals(httpMethod)) {
                operLog.setBusinessType(1); // 新增
                operLog.setOperatorType(1);
            } else if ("PUT".equals(httpMethod)) {
                operLog.setBusinessType(2); // 修改
                operLog.setOperatorType(2);
            } else if ("DELETE".equals(httpMethod)) {
                operLog.setBusinessType(3); // 删除
                operLog.setOperatorType(3);
            } else {
                operLog.setBusinessType(0); // 其它
                operLog.setOperatorType(0);
            }

            // 异步保存日志
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
            
        } catch (Exception exp) {
            log.error("操作日志记录异常: {}", exp.getMessage());
        }
    }

    private boolean isExcludedUrl(HttpServletRequest request) {
        if (request == null) return false;
        
        String requestURI = request.getRequestURI();
        for (String pattern : EXCLUDE_URL_PATTERNS) {
            if (AntPathMatcher.DEFAULT_PATH_SEPARATOR.equals(pattern) || 
                new AntPathMatcher().match(pattern, requestURI)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (o != null && !isFilterObject(o)) {
                    try {
                        // 简单过滤敏感字段
                        String jsonObj = filterSensitiveFields(o);
                        params.append(jsonObj).append(" ");
                    } catch (Exception ignored) {
                        // 无法序列化的对象转为字符串
                        params.append(o.toString()).append(" ");
                    }
                }
            }
        }
        return params.toString().trim();
    }

    /**
     * 简单过滤敏感字段
     */
    private String filterSensitiveFields(Object obj) throws Exception {
        String json = objectMapper.writeValueAsString(obj);
        
        // 对简单对象进行敏感字段过滤
        if (json.startsWith("{") && json.endsWith("}")) {
            StringBuilder filtered = new StringBuilder();
            String[] pairs = json.substring(1, json.length() - 1).split(",");
            
            for (String pair : pairs) {
                String[] keyValue = pair.split(":", 2);
                if (keyValue.length == 2) {
                    String key = keyValue[0].trim().replace("\"", "");
                    if (!EXCLUDE_PROPERTIES.contains(key)) {
                        if (filtered.length() > 0) filtered.append(",");
                        filtered.append(pair);
                    }
                }
            }
            
            return "{" + filtered.toString() + "}";
        }
        
        return json;
    }

    // 排除特殊对象
    public boolean isFilterObject(final Object o) {
        return o instanceof HttpServletRequest || o instanceof jakarta.servlet.http.HttpServletResponse;
    }
}